Lær at implementere React Error Boundaries med hooks for elegant at håndtere fejl ved ressourceindlæsning, hvilket forbedrer brugeroplevelse og applikationsstabilitet.
Robust Ressourceindlæsning i React: Behersk Fejlgrænser med Hooks
I moderne webapplikationer er asynkron indlæsning af ressourcer en almindelig praksis. Uanset om det er hentning af data fra et API, indlæsning af billeder eller import af moduler, er håndtering af potentielle fejl under ressourceindlæsning afgørende for en gnidningsfri brugeroplevelse. React Error Boundaries (Fejlgrænser) giver en mekanisme til at fange JavaScript-fejl hvor som helst i deres underordnede komponenttræ, logge disse fejl og vise en fallback-brugergrænseflade i stedet for at lade hele applikationen gå ned. Denne artikel udforsker, hvordan man effektivt bruger Error Boundaries i kombination med React Hooks til at håndtere fejl ved ressourceindlæsning.
Forståelse af Fejlgrænser (Error Boundaries)
Før React 16 ville uhåndterede JavaScript-fejl under komponent-rendering ødelægge Reacts interne tilstand og forårsage kryptiske fejl ved efterfølgende renderings. Error Boundaries løser dette ved at fungere som catch-all-blokke for fejl, der opstår i deres underordnede komponenter. De er React-komponenter, der implementerer enten den ene eller begge af de følgende livscyklusmetoder:
static getDerivedStateFromError(error): Denne statiske metode påkaldes, efter en fejl er blevet kastet af en underordnet komponent. Den modtager den kastede fejl som argument og returnerer en værdi for at opdatere komponentens tilstand.componentDidCatch(error, info): Denne livscyklusmetode påkaldes, efter en fejl er blevet kastet af en underordnet komponent. Den modtager den kastede fejl som argument samt et objekt, der indeholder information om, hvilken komponent der kastede fejlen. Du kan bruge den til at logge fejlinformation.
Vigtigt er, at Error Boundaries kun fanger fejl i renderingsfasen, i livscyklusmetoder og i konstruktører i hele træet under dem. De fanger ikke fejl for:
- Hændelseshåndterere (event handlers) (lær mere i afsnittet nedenfor)
- Asynkron kode (f.eks.
setTimeoutellerrequestAnimationFramecallbacks) - Server-side rendering
- Fejl, der kastes i selve Error Boundary-komponenten (i stedet for dens børn)
Fejlgrænser og React Hooks: En Stærk Kombination
Mens klassekomponenter traditionelt blev brugt til at implementere Error Boundaries, tilbyder React Hooks en mere koncis og funktionel tilgang. Vi kan oprette en genanvendelig useErrorBoundary hook, der indkapsler fejlhåndteringslogikken og giver en bekvem måde at wrappe komponenter, der kan kaste fejl under ressourceindlæsning.
Oprettelse af en Brugerdefineret useErrorBoundary Hook
Her er et eksempel på en useErrorBoundary hook:
import { useState, useCallback } from 'react';
function useErrorBoundary() {
const [error, setError] = useState(null);
const resetError = useCallback(() => {
setError(null);
}, []);
const captureError = useCallback((e) => {
setError(e);
}, []);
const ErrorBoundary = useCallback(({ children, fallback }) => {
if (error) {
return fallback ? fallback : An error occurred: {error.message || String(error)};
}
return children;
}, [error]);
return { ErrorBoundary, captureError, error, resetError };
}
export default useErrorBoundary;
Forklaring:
useState: Vi brugeruseStatetil at håndtere fejltilstanden. Den sætter oprindeligt fejlen tilnull.useCallback: Vi brugeruseCallbacktil at memoize funktionerneresetErrorogcaptureError. Dette er god praksis for at forhindre unødvendige re-renders, hvis disse funktioner videregives som props.ErrorBoundaryKomponent: Dette er en funktionel komponent oprettet meduseCallback, der tagerchildrenog en valgfrifallbackprop. Hvis der findes en fejl i tilstanden, render den enten den angivnefallback-komponent eller en standardfejlmeddelelse. Ellers render den sine børn. Dette fungerer som vores Fejlgrænse. Afhængighedsarrayet `[error]` sikrer, at den re-render, når `error`-tilstanden ændres.captureErrorFunktion: Denne funktion bruges til at sætte fejltilstanden. Du vil kalde denne inden i entry...catch-blok, når du indlæser ressourcer.resetErrorFunktion: Denne funktion rydder fejltilstanden, hvilket giver komponenten mulighed for at re-render sine børn (og potentielt forsøge at indlæse ressourcen igen).
Implementering af Ressourceindlæsning med Fejlhåndtering
Lad os nu se, hvordan man bruger denne hook til at håndtere fejl ved ressourceindlæsning. Overvej en komponent, der henter brugerdata fra et API:
import React, { useState, useEffect } from 'react';
import useErrorBoundary from './useErrorBoundary';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const { ErrorBoundary, captureError, error, resetError } = useErrorBoundary();
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setUser(data);
} catch (e) {
captureError(e);
}
};
fetchData();
}, [userId, captureError]);
if (error) {
return (
Failed to load user data. {user.name}
Email: {user.email}
{/* Other user details */}Forklaring:
- Vi importerer
useErrorBoundary-hook'en. - Vi kalder hook'en for at få
ErrorBoundary-komponenten,captureError-funktionen,error-tilstanden ogresetError-funktionen. - Inde i
useEffect-hook'en wrapper vi API-kaldet i entry...catch-blok. - Hvis der opstår en fejl under API-kaldet, kalder vi
captureError(e)for at sætte fejltilstanden. - Hvis
error-tilstanden er sat, render viErrorBoundary-komponenten. Vi giver en brugerdefineretfallback-prop, der viser en fejlmeddelelse og en "Prøv igen"-knap. Ved at klikke på knappen kaldesresetErrorfor at rydde fejltilstanden, hvilket udløser en re-render og et nyt forsøg på at hente dataene. - Hvis der ikke opstod nogen fejl, og brugerdataene er indlæst, render vi brugerprofiloplysningerne.
Håndtering af Forskellige Typer af Ressourceindlæsningsfejl
Forskellige typer af fejl ved ressourceindlæsning kan kræve forskellige håndteringsstrategier. Her er nogle almindelige scenarier og hvordan man håndterer dem:
Netværksfejl
Netværksfejl opstår, når klienten ikke kan oprette forbindelse til serveren (f.eks. på grund af et netværksudfald eller en server, der er nede). Eksemplet ovenfor håndterer allerede grundlæggende netværksfejl ved hjælp af `response.ok`. Du vil måske tilføje mere sofistikeret fejlfinding, for eksempel:
//Inde i fetchData funktionen
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
// Overvej at tilføje specifik håndtering af fejlkoder
if (response.status === 404) {
throw new Error("User not found");
} else if (response.status >= 500) {
throw new Error("Server error. Please try again later.");
} else {
throw new Error(`HTTP error! status: ${response.status}`);
}
}
const data = await response.json();
setUser(data);
} catch (error) {
if (error.message === 'Failed to fetch') {
// Sandsynligvis en netværksfejl
captureError(new Error('Network error. Please check your internet connection.'));
} else {
captureError(error);
}
}
I dette tilfælde kan du vise en besked til brugeren, der indikerer, at der er et problem med netværksforbindelsen, og foreslå, at de tjekker deres internetforbindelse.
API-fejl
API-fejl opstår, når serveren returnerer et fejlsvar (f.eks. en 400 Bad Request eller en 500 Internal Server Error). Som vist ovenfor kan du tjekke `response.status` og håndtere disse fejl korrekt.
Data-Parsingfejl
Data-parsingfejl opstår, når svaret fra serveren ikke er i det forventede format og ikke kan parses (f.eks. ugyldig JSON). Du kan håndtere disse fejl ved at wrappe response.json()-kaldet i en try...catch-blok:
//Inde i fetchData funktionen
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setUser(data);
} catch (error) {
if (error instanceof SyntaxError) {
captureError(new Error('Failed to parse data from server.'));
} else {
captureError(error);
}
}
Billedindlæsningsfejl
For billedindlæsning kan du bruge onError event handleren på <img>-tagget:
function MyImage({ src, alt }) {
const { ErrorBoundary, captureError } = useErrorBoundary();
const [imageLoaded, setImageLoaded] = useState(false);
const handleImageLoad = () => {
setImageLoaded(true);
};
const handleImageError = (e) => {
captureError(new Error(`Failed to load image: ${src}`));
};
return (
Failed to load image.